#include "syntax_parser.h"
#include "errors_warnings.h"

#include "main_container.h"
extern MainContainer * CompilerData;

//<class_body> ~ <constructor_definition>  {NAME, public, private, protected}
//
//<class_body> ~ <field_definition>  {int, float, bool, state, time, void, Facility, Storage, Queue, ADTable, Group, OutputStream, NAME, const, public, private, protected, static}
//
//<class_body> ~ <method_definition>  {numeric, start, process, int, float, bool, state, time, void, Facility, Storage, Queue, ADTable, Group, OutputStream, NAME, abstract, virtual, public, private, protected, override, static, final}
//
//<class_body> ~ <property_definition>  {int, float, bool, state, time, void, Facility, Storage, Queue, ADTable, Group, OutputStream, NAME, public, protected, private, static, abstract, override, virtual, final}
//
//<class_body> ~ <enum_definition>  {enum}
//
//<class_body> ~ <class_definition>  {class, template, abstract, public, private, protected, final}
//
void Parser::Parse_ClassBody(SyntaxTreeNode * parent)
{
#ifdef TRACE
    CompilerData->LogTrace->ReportEntry("Parse_ClassBody");
#endif

    LexicUnit unit;

    // get current lexic unit
    unit = CompilerData->Units->GetLexicUnit(this->CurrentLexicPosition);

    // ako se lekisčka jedinka nalazi u skupu Primjeni produkcije koja nije epsilon {enum} 
    // first check for unique productions
    if (unit.type == LUTypeKeyword && unit.subtype == KeywordEnum)
    {
        // write type of production in this node
        //<class_body> ~ <enum_definition>   {enum}
        parent->Production = Gr_ClassBody__EnumDefinition;

        // process nonterminal symbol '<enum_definition>'
        SyntaxTreeNode * newEnumDefinitionNode = new SyntaxTreeNode();
        parent->Nodes->Add(newEnumDefinitionNode);
        Parse_EnumDefinition(newEnumDefinitionNode);
    }
    else if (unit.type == LUTypeKeyword && (unit.subtype == KeywordConst||unit.subtype == KeywordTrace))
    {
        // write type of production in this node
        //<class_body> ~ <field_definition>  {..., const, ...}
        parent->Production = Gr_ClassBody__FieldDefinition;

        // process nonterminal symbol '<field_definition>'
        SyntaxTreeNode * newFieldDefinitionNode = new SyntaxTreeNode();
        parent->Nodes->Add(newFieldDefinitionNode);
        Parse_FieldDefinition(newFieldDefinitionNode);
    }
    else if (unit.type == LUTypeKeyword &&
        (unit.subtype == KeywordNumeric || 
        unit.subtype == KeywordStart || 
        unit.subtype == KeywordProcess))
    {
        // write type of production in this node
        //<class_body> ~ <method_definition> {numeric, start, process, ...}
        parent->Production = Gr_ClassBody__MethodDefinition;

        // process nonterminal symbol '<method_definition>'
        SyntaxTreeNode * newMethodDefinitionNode = new SyntaxTreeNode();
        parent->Nodes->Add(newMethodDefinitionNode);
        Parse_MethodDefinition(newMethodDefinitionNode);
    }        
    else if (unit.type == LUTypeKeyword && 
        (unit.subtype == KeywordClass || 
        unit.subtype == KeywordTemplate
        ))
    {

        // write type of production in this node
        // <class_body> ~ <class_definition>  {class, template, ...}
        parent->Production = Gr_ClassBody__ClassDefinition;

        // process nonterminal symbol '<class_definition>'
        SyntaxTreeNode * newClassDefinitionNode = new SyntaxTreeNode();
        parent->Nodes->Add(newClassDefinitionNode);
        Parse_ClassDefinition(newClassDefinitionNode);
    }
    else if (unit.type == LUTypeKeyword && 
        (unit.subtype == KeywordVirtual ||
        unit.subtype == KeywordFinal || 
        unit.subtype == KeywordOverride))
    {
        // Ambiguous select sets
        //<class_body> ~ <method_definition>   {..., virtual, final, override, ...}
        //<class_body> ~ <property_definition>  {..., virtual, final, override, ...}
         ////<class_body> ~ <class_definition>
        if (isMethodProduction(this->CurrentLexicPosition))
        {
            // write type of production in this node
            //<class_body> ~ <method_definition> 
            parent->Production = Gr_ClassBody__MethodDefinition;

            // process nonterminal symbol '<method_definition>'
            SyntaxTreeNode * newMethodDefinitionNode = new SyntaxTreeNode();
            parent->Nodes->Add(newMethodDefinitionNode);
            Parse_MethodDefinition(newMethodDefinitionNode);
        }
        else if (isPropertyProduction(this->CurrentLexicPosition))
        {
            // write type of production in this node
            //<class_body> ~ <property_definition> 
            parent->Production = Gr_ClassBody__PropertyDefinition;

            // process nonterminal symbol '<property_definition>'
            SyntaxTreeNode * newPropertyDefinitionNode = new SyntaxTreeNode();
            parent->Nodes->Add(newPropertyDefinitionNode);
            Parse_PropertyDefinition(newPropertyDefinitionNode);             
        }

        else if (isClassProduction(this->CurrentLexicPosition))
        {
            // write type of production in this node
            //<class_body> ~ <class_definition> 
            parent->Production = Gr_ClassBody__ClassDefinition;

            // process nonterminal symbol '<class_definition>'
            SyntaxTreeNode * newPropertyDefinitionNode = new SyntaxTreeNode();
            parent->Nodes->Add(newPropertyDefinitionNode);
            Parse_ClassDefinition(newPropertyDefinitionNode);             
        }
        else
        {
            // ERROR has been found
            // REPORT ERROR
            CompilerData->ErrorWarningReport->ReportError(&unit, ERR_SYN_NO_CLASS_BODY_MATCH);

            // This is recovoring from error
            this->CurrentLexicPosition++;
        }

    }
    else if (unit.type == LUTypeKeyword && unit.subtype == KeywordAbstract)
    {
        // Ambiguous select sets
        //<class_body> ~ <method_definition>   {..., abstract, ...}
        //<class_body> ~ <property_definition>   {..., abstract, ...}
        //<class_body> ~ <class_definition>   {..., abstract, ...}
        if (isMethodProduction(this->CurrentLexicPosition))
        {
            // write type of production in this node
            //<class_body> ~ <method_definition> 
            parent->Production = Gr_ClassBody__MethodDefinition;

            // process nonterminal symbol '<method_definition>'
            SyntaxTreeNode * newMethodDefinitionNode = new SyntaxTreeNode();
            parent->Nodes->Add(newMethodDefinitionNode);
            Parse_MethodDefinition(newMethodDefinitionNode);              
        }
        else if (isPropertyProduction(this->CurrentLexicPosition))
        {

            // write type of production in this node
            //<class_body> ~ <property_definition> 
            parent->Production = Gr_ClassBody__PropertyDefinition;

            // process nonterminal symbol '<property_definition>'
            SyntaxTreeNode * newPropertyDefinitionNode = new SyntaxTreeNode();
            parent->Nodes->Add(newPropertyDefinitionNode);
            Parse_PropertyDefinition(newPropertyDefinitionNode);
        }
        else if (isClassProduction(this->CurrentLexicPosition))
        {
            // write type of production in this node
            //<class_body> ~ <class_definition> 
            parent->Production = Gr_ClassBody__ClassDefinition;

            // process nonterminal symbol '<class_definition>'
            SyntaxTreeNode * newClassDefinitionNode = new SyntaxTreeNode();
            parent->Nodes->Add(newClassDefinitionNode);
            Parse_ClassDefinition(newClassDefinitionNode);
        }
        else
        {
            // ERROR has been found
            // REPORT ERROR
            CompilerData->ErrorWarningReport->ReportError(&unit, ERR_SYN_NO_CLASS_BODY_MATCH);

            // This is recovoring from error
            this->CurrentLexicPosition++;
        }

    }    
    else if (unit.type == LUTypeKeyword &&
        (unit.subtype == KeywordStatic ||
        unit.subtype == KeywordInt || unit.subtype == KeywordFloat || 
        unit.subtype == KeywordBool || unit.subtype == KeywordState || 
        unit.subtype == KeywordTime || unit.subtype == KeywordVoid || 
        unit.subtype == KeywordFacility || unit.subtype == KeywordStorage || 
        unit.subtype == KeywordQueue || unit.subtype == KeywordADTable || 
        unit.subtype == KeywordGroup || unit.subtype == KeywordOutputStream))
    {
        // Ambiguous select sets
        //<class_body> ~ <field_definition>  {..., int, float, bool, state, time, void, Facility, Storage, Queue, ADTable, Group, OutputStream, ...}
        //<class_body> ~ <method_definition>   {..., int, float, bool, state, time, void, Facility, Storage, Queue, ADTable, Group, OutputStream, ...}
        //<class_body> ~ <property_definition>   {..., int, float, bool, state, time, void, Facility, Storage, Queue, ADTable, Group, OutputStream, ...}
        if (isFieldProduction(this->CurrentLexicPosition))
        {
            // write type of production in this node
            //<class_body> ~ <field_definition> 
            parent->Production = Gr_ClassBody__FieldDefinition;

            // process nonterminal symbol '<field_definition>'
            SyntaxTreeNode * newFieldDefinitionNode = new SyntaxTreeNode();
            parent->Nodes->Add(newFieldDefinitionNode);
            Parse_FieldDefinition(newFieldDefinitionNode);             
        }
        else if (isPropertyProduction(this->CurrentLexicPosition))
        {
            // write type of production in this node
            //<class_body> ~ <property_definition> 
            parent->Production = Gr_ClassBody__PropertyDefinition;

            // process nonterminal symbol '<property_definition>'
            SyntaxTreeNode * newPropertyDefinitionNode = new SyntaxTreeNode();
            parent->Nodes->Add(newPropertyDefinitionNode);
            Parse_PropertyDefinition(newPropertyDefinitionNode);

        }
        else if (isMethodProduction(this->CurrentLexicPosition))
        {
            // write type of production in this node
            //<class_body> ~ <method_definition> 
            parent->Production = Gr_ClassBody__MethodDefinition;

            // process nonterminal symbol '<method_definition>'
            SyntaxTreeNode * newMethodDefinitionNode = new SyntaxTreeNode();
            parent->Nodes->Add(newMethodDefinitionNode);
            Parse_MethodDefinition(newMethodDefinitionNode);   
        }
        else
        {
            // ERROR has been found
            // REPORT ERROR
            CompilerData->ErrorWarningReport->ReportError(&unit, ERR_SYN_NO_CLASS_BODY_MATCH);

            // This is recovoring from error
            this->CurrentLexicPosition++;
        }            
    }        
    else if (unit.type == LUTypeIdentifier)
    {
        // Ambiguous select sets
        //<class_body> ~ <constructor_definition>   {..., NAME, ...}
        //<class_body> ~ <field_definition>   {..., NAME, ...}
        //<class_body> ~ <method_definition>  {..., NAME, ...}
        //<class_body> ~ <property_definition>   {..., Name, ...}
        if (isFieldProduction(this->CurrentLexicPosition))
        {
            // write type of production in this node
            //<class_body> ~ <field_definition> 
            parent->Production = Gr_ClassBody__FieldDefinition;

            // process nonterminal symbol '<field_definition>'
            SyntaxTreeNode * newFieldDefinitionNode = new SyntaxTreeNode();
            parent->Nodes->Add(newFieldDefinitionNode);
            Parse_FieldDefinition(newFieldDefinitionNode);             
        }
        else if (isPropertyProduction(this->CurrentLexicPosition))
        {
            // write type of production in this node
            //<class_body> ~ <property_definition> 
            parent->Production = Gr_ClassBody__PropertyDefinition;

            // process nonterminal symbol '<property_definition>'
            SyntaxTreeNode * newPropertyDefinitionNode = new SyntaxTreeNode();
            parent->Nodes->Add(newPropertyDefinitionNode);
            Parse_PropertyDefinition(newPropertyDefinitionNode);
        }
        else if (isMethodProduction(this->CurrentLexicPosition))
        {
            // write type of production in this node
            //<class_body> ~ <method_definition> 
            parent->Production = Gr_ClassBody__MethodDefinition;

            // process nonterminal symbol '<method_definition>'
            SyntaxTreeNode * newMethodDefinitionNode = new SyntaxTreeNode();
            parent->Nodes->Add(newMethodDefinitionNode);
            Parse_MethodDefinition(newMethodDefinitionNode); 
        }
        else
        {
            int position = this->CurrentLexicPosition + 1;
            LexicUnit nextUnit;
            // check if we can match <constructor_definition>  
            //<constructor_definition> -> <constr_mod> NAME ( <arguments> ) <constr_inherit> <constr_body> 
            //      {public, private, protected, NAME} 

            nextUnit = CompilerData->Units->GetLexicUnit(position);
            if (nextUnit.type == LUTypeSpecialType && nextUnit.subtype == endOfFile)
            {
                // ERROR has been found
                // REPORT ERROR
                // MAKE LOGICAL RECOVERING
            }


            // parse '(', if found then this is <constructor_definition>
            if (nextUnit.type == LUTypeSpecialCharacter && nextUnit.subtype == SpecialCharacterLeftParenthesis)
            {

				parent->Production = Gr_ClassBody__ConstructorDefinition;
                // process nonterminal symbol '<constructor_definition>'
                SyntaxTreeNode * newConstructorDefinitionNode = new SyntaxTreeNode();
                parent->Nodes->Add(newConstructorDefinitionNode);
                Parse_ConstructorDefinition(newConstructorDefinitionNode);

            }
            else
            {
                // ERROR has been found
                // REPORT ERROR
                CompilerData->ErrorWarningReport->ReportError(&unit, ERR_SYN_NO_CLASS_BODY_MATCH);

                // This is recovoring from error
                this->CurrentLexicPosition++;
            }
        }          
    }
    else if (unit.type == LUTypeKeyword &&
        (unit.subtype == KeywordPublic ||
        unit.subtype == KeywordPrivate || 
        unit.subtype == KeywordProtected))
    {
        // Ambiguous select sets
        //<class_body> ~ <constructor_definition>   {..., public, private, protected, ...}
        //<class_body> ~ <field_definition>   {..., public, private, protected, ...}
        //<class_body> ~ <method_definition>   {..., public, private, protected, ...}
        //<class_body> ~ <property_definition>   {..., public, private, protected, ...}
        //<class_body> ~ <class_definition>   {..., public, private, protected, ...}
        if (isFieldProduction(this->CurrentLexicPosition))
        {
            // write type of production in this node
            //<class_body> ~ <field_definition> 
            parent->Production = Gr_ClassBody__FieldDefinition;

            // process nonterminal symbol '<field_definition>'
            SyntaxTreeNode * newFieldDefinitionNode = new SyntaxTreeNode();
            parent->Nodes->Add(newFieldDefinitionNode);
            Parse_FieldDefinition(newFieldDefinitionNode);                
        }
        else if (isPropertyProduction(this->CurrentLexicPosition))
        {
            // write type of production in this node
            //<class_body> ~ <property_definition> 
            parent->Production = Gr_ClassBody__PropertyDefinition;

            // process nonterminal symbol '<property_definition>'
            SyntaxTreeNode * newPropertyDefinitionNode = new SyntaxTreeNode();
            parent->Nodes->Add(newPropertyDefinitionNode);
            Parse_PropertyDefinition(newPropertyDefinitionNode);
        }
        else if (isMethodProduction(this->CurrentLexicPosition))
        {
            // write type of production in this node
            //<class_body> ~ <method_definition> 
            parent->Production = Gr_ClassBody__MethodDefinition;

            // process nonterminal symbol '<method_definition>'
            SyntaxTreeNode * newMethodDefinitionNode = new SyntaxTreeNode();
            parent->Nodes->Add(newMethodDefinitionNode);
            Parse_MethodDefinition(newMethodDefinitionNode);  
        }
        else if (isClassProduction(this->CurrentLexicPosition))
        {
            // write type of production in this node
            //<class_body> ~ <class_definition> 
            parent->Production = Gr_ClassBody__ClassDefinition;

            // process nonterminal symbol '<class_definition>'
            SyntaxTreeNode * newClassDefinitionNode = new SyntaxTreeNode();
            parent->Nodes->Add(newClassDefinitionNode);
            Parse_ClassDefinition(newClassDefinitionNode);
        }
        else
        {
            int position = this->CurrentLexicPosition + 1;
            LexicUnit nextUnit;
            // check if we can match <constructor_definition>  
            //<constructor_definition> -> <constr_mod> NAME ( <arguments> ) <constr_inherit> <constr_body> 
            //      {public, private, protected, NAME} 

            nextUnit = CompilerData->Units->GetLexicUnit(position);
            if (nextUnit.type == LUTypeSpecialType && nextUnit.subtype == endOfFile)
            {
                // ERROR has been found
                // REPORT ERROR
                // MAKE LOGICAL RECOVERING
				#ifdef TRACE
					 CompilerData->LogTrace->ReportExit("Parse_ClassBody");
				#endif
                return;
            }

            // parse NAME, if found then this might be <constructor_definition>
            if (nextUnit.type == LUTypeIdentifier)
            {
                position++;
                nextUnit = CompilerData->Units->GetLexicUnit(position);
                if (nextUnit.type == LUTypeSpecialType && nextUnit.subtype == endOfFile)
                {
                    // ERROR has been found
                    // REPORT ERROR
                    // MAKE LOGICAL RECOVERING
					#ifdef TRACE
						CompilerData->LogTrace->ReportExit("Parse_ClassBody");
					#endif
                    return;
                }

                // parse '(', if found then this is <constructor_definition>
                if (nextUnit.type == LUTypeSpecialCharacter && nextUnit.subtype == SpecialCharacterLeftParenthesis)
                {
					parent->Production = Gr_ClassBody__ConstructorDefinition;
                    // process nonterminal symbol '<constructor_definition>'
                    SyntaxTreeNode * newConstructorDefinitionNode = new SyntaxTreeNode();
                    parent->Nodes->Add(newConstructorDefinitionNode);
                    Parse_ConstructorDefinition(newConstructorDefinitionNode);


                }
                else
                {
                    // ERROR has been found
                    // REPORT ERROR
                    CompilerData->ErrorWarningReport->ReportError(&unit, ERR_SYN_NO_CLASS_BODY_MATCH);

                    // This is recovoring from error
                    this->CurrentLexicPosition++;
                }
            }
            else
            {
                // ERROR has been found
                // REPORT ERROR
                CompilerData->ErrorWarningReport->ReportError(&unit, ERR_SYN_NO_CLASS_BODY_MATCH);

                // This is recovoring from error
                this->CurrentLexicPosition++;
            }
        }     
    }
    // Ako nije prepoznat znak niti iz jednog skupa primjena, radi se o grešci 
    //i treba ju prijaviti i izvesti logičan oporavak od pogreške
    else
    {
        // ERROR has been found
        // REPORT ERROR
        CompilerData->ErrorWarningReport->ReportError(&unit, ERR_SYN_NO_CLASS_BODY_ADD_BEGINING);

        // This is recovoring from error
        this->CurrentLexicPosition++;

    }




#ifdef TRACE
    CompilerData->LogTrace->ReportExit("Parse_ClassBody");
#endif

}

//<class_body_add> ~ <class_body> <class_body_add>  {trace, NAME, numeric, enum, class, template, public, private, protected, int, float, bool, state, time, void, Facility, Storage, Queue, ADTable, Group, OutputStream, start, process, abstract, const, static, virtual, override, final}
//
//<class_body_add> ~ ? {}}
//
void Parser::Parse_ClassBodyAdd(SyntaxTreeNode * parent)
{
#ifdef TRACE
    CompilerData->LogTrace->ReportEntry("Parse_ClassBodyAdd");
#endif

    LexicUnit unit;

    do
    {
#ifdef TRACE
        CompilerData->LogTrace->ReportLine("Getting lexic unit.");
#endif

        // get current lexic unit
        unit = CompilerData->Units->GetLexicUnit(this->CurrentLexicPosition);

        // ako se lekisčka jedinka nalazi u skupu Primjeni produkcije koja nije epsilon {,} 
        if (unit.type == LUTypeIdentifier || 
            (unit.type == LUTypeKeyword && 
            (unit.subtype == KeywordNumeric || unit.subtype == KeywordEnum || unit.subtype == KeywordClass ||unit.subtype == KeywordTrace ||
            unit.subtype == KeywordTemplate || unit.subtype == KeywordPublic || unit.subtype == KeywordPrivate ||
            unit.subtype == KeywordProtected || unit.subtype == KeywordInt || unit.subtype == KeywordFloat ||
            unit.subtype == KeywordBool || unit.subtype == KeywordState || unit.subtype == KeywordTime ||
            unit.subtype == KeywordVoid || unit.subtype == KeywordFacility || unit.subtype == KeywordStorage ||
            unit.subtype == KeywordQueue || unit.subtype == KeywordADTable || unit.subtype == KeywordGroup ||
            unit.subtype == KeywordOutputStream || unit.subtype == KeywordStart || unit.subtype == KeywordProcess ||
            unit.subtype == KeywordAbstract  || unit.subtype == KeywordConst ||
            unit.subtype == KeywordNumeric || unit.subtype == KeywordEnum || unit.subtype == KeywordClass ||
            unit.subtype == KeywordStatic || unit.subtype == KeywordVirtual || unit.subtype == KeywordOverride ||
            unit.subtype == KeywordFinal)))
        {
            //	write type of production in this node
            //<class_body_add> ~ <class_body> <class_body_add>
            parent->Production = Gr_ClassBodyAdd__ClassBody_ClassBodyAdd;

            // process nonterminal symbol <class_body>		
            SyntaxTreeNode * newClassBodyNode = new SyntaxTreeNode();
            parent->Nodes->Add(newClassBodyNode);
            Parse_ClassBody(newClassBodyNode);

        }

        // if current lexic unit is in the select set of {}}
        // the epsilon production
        else if (unit.type == LUTypeSpecialCharacter && unit.subtype == SpecialCharacterRightCurlyBrace)
        {
            // this is epsilon part of production
            parent->Production = Gr_ClassBodyAdd__ClassBody_ClassBodyAdd;

            // new node is not needed
            break;

        }

        // the current lexical symbol was not found matching any production
        else
        {
            // ERROR has been found
            // REPORT ERROR
            CompilerData->ErrorWarningReport->ReportError(&unit, ERR_SYN_NO_CLASS_BODY_ADD_BEGINING);

            // This is recovoring from error
            this->CurrentLexicPosition++;
            break;

        }


    }
    while(1);

#ifdef TRACE
    CompilerData->LogTrace->ReportExit("Parse_ClassBodyAdd");
#endif
}